/*
 * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $"
 *
 * Character entity support code for Mini-XML, a small XML-like
 * file parsing library.
 *
 * Copyright 2003-2010 by Michael R Sweet.
 *
 * These coded instructions, statements, and computer programs are the
 * property of Michael R Sweet and are protected by Federal copyright
 * law.  Distribution and use rights are outlined in the file "COPYING"
 * which should have been included with this file.  If this file is
 * missing or damaged, see the license at:
 *
 *     http://www.minixml.org/
 *
 * Contents:
 *
 *   mxmlEntityAddCallback()    - Add a callback to convert entities to
 *                                Unicode.
 *   mxmlEntityGetName()        - Get the name that corresponds to the
 *                                character value.
 *   mxmlEntityGetValue()       - Get the character corresponding to a named
 *                                entity.
 *   mxmlEntityRemoveCallback() - Remove a callback.
 *   _mxml_entity_cb()          - Lookup standard (X)HTML entities.
 */

/*
 * Include necessary headers...
 */

#include "mxml-private.h"


/*
 * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode.
 */

int					/* O - 0 on success, -1 on failure */ mxmlEntityAddCallback(
    mxml_entity_cb_t cb)  	/* I - Callback function to add */
{
	_mxml_global_t* global = _mxml_global();
	/* Global data */

	if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(
	                                       global->entity_cbs[0])))
	{
		global->entity_cbs[global->num_entity_cbs] = cb;
		global->num_entity_cbs ++;
		return (0);
	}
	else
	{
		mxml_error("Unable to add entity callback!");
		return (-1);
	}
}


/*
 * 'mxmlEntityGetName()' - Get the name that corresponds to the character value.
 *
 * If val does not need to be represented by a named entity, NULL is returned.
 */

const char* 				/* O - Entity name or NULL */ mxmlEntityGetName(
    int val)  	/* I - Character value */
{
	switch (val)
	{
		case '&' :
			return ("amp");

		case '<' :
			return ("lt");

		case '>' :
			return ("gt");

		case '\"' :
			return ("quot");

		default :
			return (NULL);
	}
}


/*
 * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity.
 *
 * The entity name can also be a numeric constant. -1 is returned if the
 * name is not known.
 */

int					/* O - Character value or -1 on error */ mxmlEntityGetValue(
    const char* name)  	/* I - Entity name */
{
	int		i;			/* Looping var */
	int		ch;			/* Character value */
	_mxml_global_t* global = _mxml_global();
	/* Global data */

	for (i = 0; i < global->num_entity_cbs; i ++)
		if ((ch = (global->entity_cbs[i])(name)) >= 0)
		{
			return (ch);
		}

	return (-1);
}


/*
 * 'mxmlEntityRemoveCallback()' - Remove a callback.
 */

void mxmlEntityRemoveCallback(
    mxml_entity_cb_t cb)  	/* I - Callback function to remove */
{
	int		i;			/* Looping var */
	_mxml_global_t* global = _mxml_global();
	/* Global data */

	for (i = 0; i < global->num_entity_cbs; i ++)
		if (cb == global->entity_cbs[i])
		{
			/*
			 * Remove the callback...
			 */
			global->num_entity_cbs --;

			if (i < global->num_entity_cbs)
				memmove(global->entity_cbs + i, global->entity_cbs + i + 1,
				        (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0]));

			return;
		}
}


/*
 * '_mxml_entity_cb()' - Lookup standard (X)HTML entities.
 */

int					/* O - Unicode value or -1 */ _mxml_entity_cb(const char*
        name)  	/* I - Entity name */
{
	int	diff,				/* Difference between names */
	    current,			/* Current entity in search */
	    first,				/* First entity in search */
	    last;				/* Last entity in search */
	static const struct
	{
		const char*	name;			/* Entity name */
		int		val;			/* Character value */
	}	entities[] =
	{
		{ "AElig",		198 },
		{ "Aacute",		193 },
		{ "Acirc",		194 },
		{ "Agrave",		192 },
		{ "Alpha",		913 },
		{ "Aring",		197 },
		{ "Atilde",		195 },
		{ "Auml",		196 },
		{ "Beta",		914 },
		{ "Ccedil",		199 },
		{ "Chi",		935 },
		{ "Dagger",		8225 },
		{ "Delta",		916 },
		{ "Dstrok",		208 },
		{ "ETH",		208 },
		{ "Eacute",		201 },
		{ "Ecirc",		202 },
		{ "Egrave",		200 },
		{ "Epsilon",	917 },
		{ "Eta",		919 },
		{ "Euml",		203 },
		{ "Gamma",		915 },
		{ "Iacute",		205 },
		{ "Icirc",		206 },
		{ "Igrave",		204 },
		{ "Iota",		921 },
		{ "Iuml",		207 },
		{ "Kappa",		922 },
		{ "Lambda",		923 },
		{ "Mu",		924 },
		{ "Ntilde",		209 },
		{ "Nu",		925 },
		{ "OElig",		338 },
		{ "Oacute",		211 },
		{ "Ocirc",		212 },
		{ "Ograve",		210 },
		{ "Omega",		937 },
		{ "Omicron",	927 },
		{ "Oslash",		216 },
		{ "Otilde",		213 },
		{ "Ouml",		214 },
		{ "Phi",		934 },
		{ "Pi",		928 },
		{ "Prime",		8243 },
		{ "Psi",		936 },
		{ "Rho",		929 },
		{ "Scaron",		352 },
		{ "Sigma",		931 },
		{ "THORN",		222 },
		{ "Tau",		932 },
		{ "Theta",		920 },
		{ "Uacute",		218 },
		{ "Ucirc",		219 },
		{ "Ugrave",		217 },
		{ "Upsilon",	933 },
		{ "Uuml",		220 },
		{ "Xi",		926 },
		{ "Yacute",		221 },
		{ "Yuml",		376 },
		{ "Zeta",		918 },
		{ "aacute",		225 },
		{ "acirc",		226 },
		{ "acute",		180 },
		{ "aelig",		230 },
		{ "agrave",		224 },
		{ "alefsym",	8501 },
		{ "alpha",		945 },
		{ "amp",		'&' },
		{ "and",		8743 },
		{ "ang",		8736 },
		{ "apos",           '\'' },
		{ "aring",		229 },
		{ "asymp",		8776 },
		{ "atilde",		227 },
		{ "auml",		228 },
		{ "bdquo",		8222 },
		{ "beta",		946 },
		{ "brkbar",		166 },
		{ "brvbar",		166 },
		{ "bull",		8226 },
		{ "cap",		8745 },
		{ "ccedil",		231 },
		{ "cedil",		184 },
		{ "cent",		162 },
		{ "chi",		967 },
		{ "circ",		710 },
		{ "clubs",		9827 },
		{ "cong",		8773 },
		{ "copy",		169 },
		{ "crarr",		8629 },
		{ "cup",		8746 },
		{ "curren",		164 },
		{ "dArr",		8659 },
		{ "dagger",		8224 },
		{ "darr",		8595 },
		{ "deg",		176 },
		{ "delta",		948 },
		{ "diams",		9830 },
		{ "die",		168 },
		{ "divide",		247 },
		{ "eacute",		233 },
		{ "ecirc",		234 },
		{ "egrave",		232 },
		{ "empty",		8709 },
		{ "emsp",		8195 },
		{ "ensp",		8194 },
		{ "epsilon",	949 },
		{ "equiv",		8801 },
		{ "eta",		951 },
		{ "eth",		240 },
		{ "euml",		235 },
		{ "euro",		8364 },
		{ "exist",		8707 },
		{ "fnof",		402 },
		{ "forall",		8704 },
		{ "frac12",		189 },
		{ "frac14",		188 },
		{ "frac34",		190 },
		{ "frasl",		8260 },
		{ "gamma",		947 },
		{ "ge",		8805 },
		{ "gt",		'>' },
		{ "hArr",		8660 },
		{ "harr",		8596 },
		{ "hearts",		9829 },
		{ "hellip",		8230 },
		{ "hibar",		175 },
		{ "iacute",		237 },
		{ "icirc",		238 },
		{ "iexcl",		161 },
		{ "igrave",		236 },
		{ "image",		8465 },
		{ "infin",		8734 },
		{ "int",		8747 },
		{ "iota",		953 },
		{ "iquest",		191 },
		{ "isin",		8712 },
		{ "iuml",		239 },
		{ "kappa",		954 },
		{ "lArr",		8656 },
		{ "lambda",		955 },
		{ "lang",		9001 },
		{ "laquo",		171 },
		{ "larr",		8592 },
		{ "lceil",		8968 },
		{ "ldquo",		8220 },
		{ "le",		8804 },
		{ "lfloor",		8970 },
		{ "lowast",		8727 },
		{ "loz",		9674 },
		{ "lrm",		8206 },
		{ "lsaquo",		8249 },
		{ "lsquo",		8216 },
		{ "lt",		'<' },
		{ "macr",		175 },
		{ "mdash",		8212 },
		{ "micro",		181 },
		{ "middot",		183 },
		{ "minus",		8722 },
		{ "mu",		956 },
		{ "nabla",		8711 },
		{ "nbsp",		160 },
		{ "ndash",		8211 },
		{ "ne",		8800 },
		{ "ni",		8715 },
		{ "not",		172 },
		{ "notin",		8713 },
		{ "nsub",		8836 },
		{ "ntilde",		241 },
		{ "nu",		957 },
		{ "oacute",		243 },
		{ "ocirc",		244 },
		{ "oelig",		339 },
		{ "ograve",		242 },
		{ "oline",		8254 },
		{ "omega",		969 },
		{ "omicron",	959 },
		{ "oplus",		8853 },
		{ "or",		8744 },
		{ "ordf",		170 },
		{ "ordm",		186 },
		{ "oslash",		248 },
		{ "otilde",		245 },
		{ "otimes",		8855 },
		{ "ouml",		246 },
		{ "para",		182 },
		{ "part",		8706 },
		{ "permil",		8240 },
		{ "perp",		8869 },
		{ "phi",		966 },
		{ "pi",		960 },
		{ "piv",		982 },
		{ "plusmn",		177 },
		{ "pound",		163 },
		{ "prime",		8242 },
		{ "prod",		8719 },
		{ "prop",		8733 },
		{ "psi",		968 },
		{ "quot",		'\"' },
		{ "rArr",		8658 },
		{ "radic",		8730 },
		{ "rang",		9002 },
		{ "raquo",		187 },
		{ "rarr",		8594 },
		{ "rceil",		8969 },
		{ "rdquo",		8221 },
		{ "real",		8476 },
		{ "reg",		174 },
		{ "rfloor",		8971 },
		{ "rho",		961 },
		{ "rlm",		8207 },
		{ "rsaquo",		8250 },
		{ "rsquo",		8217 },
		{ "sbquo",		8218 },
		{ "scaron",		353 },
		{ "sdot",		8901 },
		{ "sect",		167 },
		{ "shy",		173 },
		{ "sigma",		963 },
		{ "sigmaf",		962 },
		{ "sim",		8764 },
		{ "spades",		9824 },
		{ "sub",		8834 },
		{ "sube",		8838 },
		{ "sum",		8721 },
		{ "sup",		8835 },
		{ "sup1",		185 },
		{ "sup2",		178 },
		{ "sup3",		179 },
		{ "supe",		8839 },
		{ "szlig",		223 },
		{ "tau",		964 },
		{ "there4",		8756 },
		{ "theta",		952 },
		{ "thetasym",	977 },
		{ "thinsp",		8201 },
		{ "thorn",		254 },
		{ "tilde",		732 },
		{ "times",		215 },
		{ "trade",		8482 },
		{ "uArr",		8657 },
		{ "uacute",		250 },
		{ "uarr",		8593 },
		{ "ucirc",		251 },
		{ "ugrave",		249 },
		{ "uml",		168 },
		{ "upsih",		978 },
		{ "upsilon",	965 },
		{ "uuml",		252 },
		{ "weierp",		8472 },
		{ "xi",		958 },
		{ "yacute",		253 },
		{ "yen",		165 },
		{ "yuml",		255 },
		{ "zeta",		950 },
		{ "zwj",		8205 },
		{ "zwnj",		8204 }
	};
	/*
	 * Do a binary search for the named entity...
	 */
	first = 0;
	last  = (int)(sizeof(entities) / sizeof(entities[0]) - 1);

	while ((last - first) > 1)
	{
		current = (first + last) / 2;

		if ((diff = strcmp(name, entities[current].name)) == 0)
		{
			return (entities[current].val);
		}
		else if (diff < 0)
		{
			last = current;
		}
		else
		{
			first = current;
		}
	}

	/*
	 * If we get here, there is a small chance that there is still
	 * a match; check first and last...
	 */

	if (!strcmp(name, entities[first].name))
	{
		return (entities[first].val);
	}
	else if (!strcmp(name, entities[last].name))
	{
		return (entities[last].val);
	}
	else
	{
		return (-1);
	}
}


/*
 * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $".
 */
